/**
 * 
 */
package com.zte.claa.inficombo.csa.app.comm.csif.mina;

import java.nio.charset.Charset;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.demux.MessageDecoder;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zte.claa.inficombo.csa.app.comm.csif.mina.msg.CSIFMessage;
import com.zte.claa.inficombo.csa.app.comm.csif.mina.msg.CSIFMsgBody;
import com.zte.claa.inficombo.csa.app.comm.csif.mina.msg.CSIFMsgHead;

/**
 * @author lyp0283000115
 *
 */
public class CSIFMsgDecoder implements MessageDecoder {
	// logger
	private static final Logger LOG = LoggerFactory.getLogger(CSIFMsgDecoder.class);
	// 字符集
	private Charset charset = null;
	// private CharsetDecoder charsetDecoder = null;

	public CSIFMsgDecoder(Charset charset) {
		this.charset = charset;
		if (this.charset == null) {
			this.charset = Charset.forName("UTF-8");
		}
		// this.charsetDecoder = this.charset.newDecoder();
	}

	/**
	 *检查是否能够解码
	 */
	public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
		// LOG.info("Current data = ["+in.getHexDump()+"]");
		if (in.remaining() < CSIFMsgHead.getHeadSize()) {
			LOG.info("Current data is not enough, wait new data coming...["+in.getHexDump()+"]");
			return MessageDecoderResult.NEED_DATA;
		}
		try {
			// begin
			in.get();
			// mainver 
			in.get();
			// subver
			in.get();
			// msgLen
			short mLen = in.getShort();
			int msgLen = Short.toUnsignedInt(mLen);
			if(msgLen > 0 && in.remaining() < msgLen){
				return MessageDecoderResult.NEED_DATA;
			}
		} catch (Exception e) {
			LOG.error("***[session=" + session.getId()+"]decodable error: "+e.getMessage(), e);
		}
		return MessageDecoderResult.OK;
	}

	/**
	 * 解码
	 */
	public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
		LOG.info("begin to decode[sessionid=" + session.getId() + "]...");
		try {
			CSIFMsgHead head = new CSIFMsgHead();
			// begin
			byte begin = in.get();
			head.setBegin((short)Byte.toUnsignedInt(begin));
			// mainver 
			byte mainVer = in.get();
			head.setMainVer((short)Byte.toUnsignedInt(mainVer));
			// subver
			byte subVer = in.get();
			head.setSubVer((short)Byte.toUnsignedInt(subVer));
			// msgLen
			short mLen = in.getShort();
			int msgLen = Short.toUnsignedInt(mLen);
			head.setMsgLen(msgLen);
			int leftSize = in.remaining();
			if(msgLen >= 1 && leftSize >= msgLen){
				// payload
				byte[] dbs = new byte[msgLen-1];
				in.get(dbs, 0, msgLen-1);
				// end
				byte end = in.get();
				String data = new String(dbs, this.charset.name());			
				CSIFMsgBody body = new CSIFMsgBody();
				body.setData(data);
				body.setEnd(end);
				CSIFMessage msg = new CSIFMessage();
				msg.setBody(body);
				msg.setHead(head);
				// 返回解码结果
				out.write(msg);
				LOG.info("OK: finish decoding[session=" + session.getId()+", msg: "+msg.toString());
				return MessageDecoderResult.OK;
			} else {
				LOG.warn("***[session=" + session.getId()+"] message is invalid...");
				this.processInvalidMsg(session, in);
			}
		} catch (Exception e) {
			LOG.error("***[session=" + session.getId()+"]decode error:"+e.getMessage(), e);
			this.processInvalidMsg(session, in);
		}
		return MessageDecoderResult.OK;
	}
	
	/**
	 * 处理掉无效消息字节流
	 * 
	 * @param session
	 * @param in
	 */
	private void processInvalidMsg(IoSession session, IoBuffer in){
		// in.getHexDump() 不会调整pos和limit等指针.
		LOG.warn("Buffer decode: data is invalid, pos="+in.position()+", limit="+in.limit()+", data="+in.getHexDump());
		int pos = in.position();
		int limit = in.limit();
		int leftSize = in.remaining();
		in.skip(leftSize);
		LOG.warn("***[session=" + session.getId()+"]skip invalid data:　pos="+pos+", limit="+limit+", leftSize="+leftSize+", left-data="+in.getHexDump());
		// 此处返回NOT_OK，MINA框架的DemuxingProtocolDecoder会将pos--limit之间的数据丢弃，此时可能会包含“跨包消息的异常丢弃”...
		// return MessageDecoderResult.NOT_OK;
		// return MessageDecoderResult.OK;
	}

	/**
	 * 
	 */
	public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
		LOG.debug("FinishDecode for session: " + session.getId());
	}
	
}
